package com.cloud.security.service.impl;

import cn.hutool.crypto.SecureUtil;
import com.cloud.common.constants.GlobalConstants;
import com.cloud.common.domain.Result;
import com.cloud.domain.LoginUser;
import com.cloud.domain.UserProject;
import com.cloud.entity.AuthBody;
import com.cloud.exception.CustomException;
import com.cloud.exception.LoginException;
import com.cloud.security.service.ISecurityService;
import com.cloud.service.RedisService;
import com.cloud.utils.JWTUtils;
import com.cloud.utils.SecurityUtils;
import com.cloud.utils.ToolUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class SecurityServiceImpl implements ISecurityService {

    @Resource
    private RedisService redisService;

    /**
     * 删除账户授权信息(退出登录)
     */
    @Override
    public void deleteLoginInfo() {
        Long userId = SecurityUtils.getUserId();
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtils.isNotEmpty(loginUser)) {
            // 移除账户授权TOKEN信息
            String codeKey = SecureUtil.md5(loginUser.getSysCode() + "/" + userId);
            redisService.del(GlobalConstants.authTokenCode + codeKey);
            // 移除账户关联项目权限信息
            redisService.del(GlobalConstants.sysUserProject + loginUser.getToken());
            // 移除账户登录信息
            redisService.del(GlobalConstants.sysLoginTokens + loginUser.getToken());
        }
    }

    /**
     * 获取Base64图形验证码
     */
    @Override
    public Result queryCode() {
        Map<String, Object> map = new HashMap<>();
        String uuid = ToolUtils.getUuid();
        // 获取图形验证码
        SchemaCaptcha captcha = getGraphCode();
        // 存入Redis 失效时间3分钟
        redisService.set(GlobalConstants.sysLoginCode + uuid, captcha.text(),
                GlobalConstants.codeExpiration, TimeUnit.MINUTES);
        map.put("imgBase64", captcha.toBase64());
        map.put("uuid", uuid);
        return Result.success("生成验证码成功", map);
    }


    /**
     * 创建Token登录授权
     */
    @Override
    public String createToken(LoginUser loginUser) {
        String token = ToolUtils.getUuid();
        loginUser.setToken(token);
        Map<String, Object> claimsMap = new HashMap<>();
        claimsMap.put(GlobalConstants.tokenKey, token);
        claimsMap.put(GlobalConstants.userId, loginUser.getUser().getUserId());
        claimsMap.put(GlobalConstants.userName, loginUser.getUser().getUserName());
        claimsMap.put(GlobalConstants.deptId, loginUser.getUser().getDeptId());
        // 刷新/设置账户缓存信息
        verifyTokenTime(loginUser);
        return JWTUtils.createToken(claimsMap);
    }

    /**
     * 刷新账户授权信息
     */
    @Override
    public void setAuthUserInfo() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtils.isNotEmpty(loginUser)) {
            // 刷新Token/账户/权限信息
            verifyUserToken(loginUser);
        }
    }

    /**
     * 设置关联账户/权限/Token
     */
    @Override
    public boolean setProjectByCode(LoginUser loginUser) {
        if (ObjectUtils.isEmpty(loginUser) || StringUtils.isEmpty(loginUser.getToken())
                || StringUtils.isEmpty(loginUser.getSysCode())) {
            throw new CustomException("Token或项目编号不能为空");
        }
        // 设置用户关联项目权限信息
        verifyUserAppInfo(loginUser);
        return true;
    }

    /**
     * 根据Token获取账户登录信息
     */
    @Override
    public Result getUserToken() {
        Map<String, Object> resultMap = new HashMap<>();
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtils.isNotEmpty(loginUser)) {
            resultMap.put("sysCode", loginUser.getSysCode());
            resultMap.put("sysUser", loginUser.getUser());
            UserProject userProject = loginUser.getUserProject();
            if (ObjectUtils.isNotEmpty(userProject)) {
                resultMap.put("roleList", userProject.getRoleList());
                resultMap.put("permissions", userProject.getPermissions());
                return Result.success("查询成功", resultMap);
            }
        }
        return Result.message(LoginException.USER_NO_AUTHORITY.getCode(),
                LoginException.USER_NO_AUTHORITY.getMsg());
    }

    /**
     * 校验接口权限
     */
    @Override
    public Result checkAuthority(String permission) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtils.isNotEmpty(loginUser)) {
            String projectKey = GlobalConstants.sysUserProject + loginUser.getToken();
            UserProject userProject = redisService.getHashKey(projectKey, loginUser.getSysCode());
            if (ObjectUtils.isNotEmpty(userProject)) {
                if (userProject.getPermissions().contains(permission)) {
                    return Result.success("鉴权成功");
                }
            }
            return Result.message(LoginException.USER_NO_AUTHORITY.getCode(),
                    LoginException.USER_NO_AUTHORITY.getMsg());
        } else {
            return Result.message(LoginException.USER_NO_LOGIN_INVALID.getCode(),
                    LoginException.USER_NO_LOGIN_INVALID.getMsg());
        }
    }

    /**
     * 根据Token获取账户信息
     */
    @Override
    public LoginUser getLoginUser(AuthBody authBody) {
        // 获取账户登录信息
        LoginUser loginUser = redisService.get(GlobalConstants.sysLoginTokens + authBody.getTokenKey());
        if (ObjectUtils.isNotEmpty(loginUser)) {
            // 根据TokenKey获取用户关联项目权限信息
            String projectKey = GlobalConstants.sysUserProject + authBody.getTokenKey();
            loginUser.setSysCode(authBody.getSysCode());
            loginUser.setUserProject(redisService.getHashKey(projectKey, authBody.getSysCode()));
            // 刷新Token/账户/权限信息
            verifyUserToken(loginUser);
        }
        return loginUser;
    }

    /**
     * 刷新账户信息缓存
     */
    private void verifyUserToken(LoginUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        long invalidTime = expireTime - currentTime;
        if (invalidTime <= GlobalConstants.MILLIS_MINUTE_TEN) {
            verifyTokenTime(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     */
    private void verifyTokenTime(LoginUser loginUser) {
        // 刷新用户关联项目权限信息
        verifyUserAppInfo(loginUser);
        // 设置账户登录信息
        loginUser.setSysCode(null);
        loginUser.setUserProject(null);
        verifyUserLoginInfo(loginUser);
    }

    /**
     * 刷新用户关联项目权限信息
     */
    private void verifyUserAppInfo(LoginUser loginUser) {
        // 根据TokenKey/SysCode缓存用户关联项目权限信息
        String projectKey = GlobalConstants.sysUserProject + loginUser.getToken();
        redisService.setHash(projectKey, loginUser.getSysCode(), loginUser.getUserProject(),
                GlobalConstants.EXPIRE_TIME, TimeUnit.MINUTES);
    }

    /**
     * 刷新账户登录信息
     */
    private void verifyUserLoginInfo(LoginUser loginUser) {
        // 根据TokenKey缓存用户登录信息
        String tokenKey = GlobalConstants.sysLoginTokens + loginUser.getToken();
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() +
                GlobalConstants.EXPIRE_TIME * GlobalConstants.MILLIS_MINUTE);
        redisService.set(tokenKey, loginUser, GlobalConstants.EXPIRE_TIME, TimeUnit.MINUTES);
    }

    /**
     * 生成图形验证码对象
     */
    private SchemaCaptcha getGraphCode() {
        // 四则算术类型, 默认是两位
        SchemaCaptcha captcha = new SchemaCaptcha(110, 52, 2);
        if (Integer.parseInt(captcha.text()) < 0) {
            return getGraphCode();
        }
        return captcha;
    }

}
